home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
Palettes
/
BarChart
/
ChartOfMatrix.m
< prev
next >
Wrap
Text File
|
1993-03-30
|
18KB
|
642 lines
/*
* BarChart, A simple multi source loadable class.
*
* Written by: Joe Freeman 7/92
*
* HSB color sweep stolen from some of Randy Nelson's code
* should use NXEqualColor() for duplicates
*
*/
#import "ChartOfMatrix.h"
#import <dpsclient/psops.h>
#import <c.h>
#import <stdio.h>
#define NUM_BOGUS 8 /* number of bars to draw when no data */
#define COM_VERSION 3.3 /* version 3 ported to 3.0 */
@implementation ChartOfMatrix
/*============================================================
*factory
*============================================================*/
+ initialize
{
[super initialize];
[ChartOfMatrix setVersion:2];
return self;
}
- (const char *)getInspectorClassName
{
return "ChartOfMatrixInspector"; }
- initFrame:(NXRect *)r
{
self = [super initFrame:r];
minSheetSet = 0.0;
maxSheetSet = 1.0;
COM_Flags.autoScale = YES;
COM_Flags.drawType = DRAW_V_BAR;
backgroundColor = NX_COLORWHITE;
highlightColor = NX_COLORBLACK;
highlightIndex = MAXINT; /* don't show on default */
COM_Flags.drawFrame = YES;
hMargin = vMargin = 15.0;
hMargin = vMargin = 5.0;
numPrototypes = 5;
COM_Flags.randomBarColors = YES;
return self;
}
- awake
{
[super awake];
highlightIndex = MAXINT;
[self registerForDraggedTypes:&NXColorPboardType count:1];
return self;
}
/*============================================================
* color dragging support
*============================================================*/
- (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
{
if ([sender draggingSourceOperationMask] & NX_DragOperationGeneric) {
return NX_DragOperationGeneric;
} else {
return NX_DragOperationNone;
}
}
- (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
{
NXColor c = NXReadColorFromPasteboard([sender draggingPasteboard]);
[self setBackgroundColor: c];
return YES;
}
/*============================================================
* instance set / query methods
*============================================================*/
- setDataSrc:anObject
{
dataSrc = anObject;
return self;
}
- setGraphType:(int)drawCode
{ COM_Flags.drawType = drawCode; [self update]; return self; }
- (int)graphType
{ return COM_Flags.drawType; }
- takeRandomColorStateFrom:sender
{ [self setRandomBarColorEnabled:[sender state]]; return self; }
- setRandomBarColorEnabled:(BOOL)flag
{ COM_Flags.randomBarColors = flag; [self update]; return self; }
- (BOOL)isRandomBarColorEnabled
{ return COM_Flags.randomBarColors; }
- takeBackgroundColorFrom:sender
{ [self setBackgroundColor: [sender color]]; return self; }
- setBackgroundColor:(NXColor)aColor
{ backgroundColor = aColor; [self update]; return self; }
- (NXColor)backgroundColor
{ return backgroundColor; }
- takeHighlightColorFrom:sender
{ [self setHighlightColor: [sender color]]; return self; }
- setHighlightColor:(NXColor)aColor
{ highlightColor = aColor; [self update]; return self; }
- (NXColor)highlightColor
{ return highlightColor; }
/* set and query the size for the margins (in points) */
- takeHMarginFrom:sender
{ hMargin = [sender floatValue]; [self update]; return self; }
- takeVMarginFrom:sender
{ vMargin = [sender floatValue]; [self update]; return self; }
- (float)hMargin
{ return hMargin; }
- (float)vMargin
{ return vMargin; }
- takeAutoScaleStateFrom:sender
{ COM_Flags.autoScale = [sender floatValue]; return self; }
- setAutoScale:(BOOL)flag
{ COM_Flags.autoScale = flag; [self update]; return self; }
- (BOOL)autoScale
{ return COM_Flags.autoScale; }
/* fancy controls */
- takeFrameStateFrom:sender
{ COM_Flags.drawFrame = [sender state]; [self update]; return self; }
- (BOOL)frameState
{ return COM_Flags.drawFrame; }
- takeNumProtosFrom:sender
{ numPrototypes= [sender intValue]; [self update]; return self; }
- (int)numProtos
{ return numPrototypes; }
- takeBorderTypeFrom:sender
{ borderType = [sender tag]; [self update]; return self; }
- (int)borderType
{ return borderType; }
/* when autoscale is off, set the min and max for the sheet */
- takeMinValueFrom:sender
{ minSheetSet = [sender floatValue]; return self; }
- takeMaxValueFrom:sender
{ maxSheetSet = [sender floatValue]; return self; }
- (double)minValue
{ return minSheetSet; }
- (double)maxValue
{ return maxSheetSet; }
/*============================================================
* target/action
*============================================================*/
- (BOOL)acceptsFirstResponder { return YES; }
- copy:sender
{
id pb = [Pasteboard new]; /* global pasteboard object */
NXStream *st; /* stream to collect data in */
char *data; /* actual data buffer */
int length; /* length of data */
int maxLength; /* (not used here) */
/* declare that we will supply a single type of data, eps */
[pb declareTypes:&NXPostScriptPboard num:1 owner:self];
/* get a stream which writes to memory */
st = NXOpenMemory (NULL, 0, NX_WRITEONLY);
/* find bounding box and then write it to the stream */
[self copyPSCodeInside:&bounds to:st];
/* get actuall data buffer form stream */
NXGetMemoryBuffer (st, &data, &length, &maxLength);
/* write data to pasteboard */
[pb writeType:NXPostScriptPboard data:data length:length ];
/* dealocate stream including it's buffer */
NXCloseMemory (st, NX_FREEBUFFER );
return self;
}
/*============================================================
* do real work
*============================================================*/
- (int)numLocations
{
int numRows,numCols;
if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){
return [dataSrc count];
} else if ([dataSrc respondsTo:@selector(selectedCell)]){
[dataSrc getNumRows:&numRows numCols:&numCols];
return MAX(numRows,numCols);
} else if (!dataSrc) {
/* put up some dummy graph on palette */
return NUM_BOGUS;
}
return 0;
}
- (float)valueOfLocation:(int)n
{
static float data[NUM_BOGUS] = {.1, .3, .2, .7, .4, .8, .5, .98};
int numRows,numCols;
float theValue;
id mrValue = [[DBValue alloc] init];
if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){
[dataSrc getValue:mrValue forProperty:mrExpression at:n];
theValue = [mrValue floatValue];
[mrValue free];
return theValue;
} else if ([dataSrc respondsTo:@selector(selectedCell)]){
[dataSrc getNumRows:&numRows numCols:&numCols];
if (numCols == 1) {
return [[dataSrc cellAt:n :0] floatValue];
} else { /* numRows == 1 */
return [[dataSrc cellAt:0 :n] floatValue];
}
} else if (!dataSrc) {
return data[n];
}
return 0.0;
}
/*============================================================
* target action loading
*============================================================*/
- plotFromMatrix:sender
{
if (!dataSrc && [sender respondsTo:@selector(selectedCell)]){
[self setDataSrc:sender];
}
[self update];
return self;
}
/*============================================================
* dbKit support
*============================================================*/
- associationContentsDidChange:association
{
mrFetchGroup = [association fetchGroup];
dataSrc = [mrFetchGroup recordList];
highlightIndex = [mrFetchGroup currentRecord];
mrExpression = [association expression];
[self update];
return self;
}
/*
** adh 7/27/92
** Do this so we redraw when values are updated in the UI
*/
- association:association setValue:(DBValue *)value
{
return [self update];
}
- associationSelectionDidChange:association
{
/* assume fetchgroup doesn't change so don't update mrFetchGroup */
highlightIndex = [[association fetchGroup] currentRecord];
return [self update];
}
- associationCurrentRecordDidDelete:association
{
/* assumes record list is the same */
return [self update];
return self;
}
/*============================================================
* do a selection with the mouse
*============================================================*/
- mouseDown:(NXEvent *)theEvent
{
NXEvent lastEvent;
NXRect rectOfBar;
NXRect drawRect;
int longSize = [self numLocations];
int index;
if ((COM_Flags.drawType != DRAW_H_BAR &&
COM_Flags.drawType != DRAW_V_BAR ) ||
![dataSrc respondsTo:@selector(getValue:forProperty:at:)]) {
NXBeep();
return nil;
}
lastEvent = *theEvent;
[self convertPoint: &lastEvent.location fromView:nil];
/* modify the mouse position to be in psuedo drawing area coords */
lastEvent.location.x -= hMargin;
lastEvent.location.y -= vMargin;
/* allow for the margins when calc'ing the bar moused down on */
drawRect = bounds;
drawRect.origin.x += hMargin;
drawRect.origin.y += vMargin;
drawRect.size.width -= (2*hMargin);
drawRect.size.height -= (2*vMargin);
for ( index = 0 ; index < longSize; index++){
if (COM_Flags.drawType == DRAW_V_BAR) {
[self calcRect:&rectOfBar ofBar:index
insideRect:&drawRect
usingMin:NX_Y(&bounds) ];
rectOfBar.origin.y = bounds.origin.y;
rectOfBar.size.height = bounds.size.height;
} else if (COM_Flags.drawType == DRAW_H_BAR) {
[self calcRect:&rectOfBar ofBar:index
insideRect:&drawRect
usingMin:NX_X(&bounds) ];
rectOfBar.origin.x = bounds.origin.x;
rectOfBar.size.width = bounds.size.width;
} else {
return nil;
}
if ([self mouse:&lastEvent.location inRect:&rectOfBar]){
[mrFetchGroup setCurrentRecord:index];
break;
}
}
return self;
}
/*============================================================
* target/action
*============================================================*/
- read:(NXTypedStream *)stream
{
int tmpScale, tmpDrawFrame, tmpDrawType;
[super read:stream];
dataSrc = NXReadObject(stream);
minField = NXReadObject(stream);
maxField = NXReadObject(stream);
meanField = NXReadObject(stream);
/* the first demo palette went out as version 1 */
if (NXTypedStreamClassVersion(stream, "ChartOfMatrix")<2) {
NXReadTypes(stream,"ffffiii",
&minSheetSet,&maxSheetSet,
&vMargin,&hMargin,
&tmpScale,
&tmpDrawType,
&tmpDrawFrame);
COM_Flags.autoScale = tmpScale;
COM_Flags.drawType = tmpDrawType;
COM_Flags.drawFrame = tmpDrawFrame;
COM_Flags.randomBarColors = YES;
} else {
NXReadTypes(stream,"ffffi",
&minSheetSet,&maxSheetSet,
&vMargin,&hMargin,
&COM_Flags);
}
backgroundColor = NXReadColor(stream);
highlightColor = NXReadColor(stream);
NXReadTypes(stream,"ii",
&numPrototypes,
&borderType);
return self;
}
- write:(NXTypedStream *)stream
{
[super write:stream];
NXWriteObjectReference(stream, dataSrc);
NXWriteObjectReference(stream, minField);
NXWriteObjectReference(stream, maxField);
NXWriteObjectReference(stream, meanField);
NXWriteTypes(stream,"ffffi",
&minSheetSet,&maxSheetSet,
&vMargin,&hMargin,
&COM_Flags);
NXWriteColor(stream, backgroundColor);
NXWriteColor(stream, highlightColor);
NXWriteTypes(stream,"ii",
&numPrototypes,
&borderType);
return self;
}
/*============================================================
*display
*============================================================*/
- calcMin:(float *)rMin andMax:(float *)rMax andMean:(float *)rMean;
{
int index;
int longSize;
float thisVal;
float sumAll = 0;
*rMin = *rMax = [self valueOfLocation:0];
longSize = [self numLocations];
/* first figure out what the maxs and mins are */
for ( index = 0 ; index < longSize; index++){
thisVal = [self valueOfLocation:index];
if (*rMin > thisVal) *rMin = thisVal;
if (*rMax < thisVal) *rMax = thisVal;
sumAll += thisVal;
}
*rMean = sumAll / longSize;
[minField setFloatValue:*rMin];
[maxField setFloatValue:*rMax];
[meanField setFloatValue:*rMean];
return self;
}
- renderVLines:(NXRect *)r min:(float )minSheetVal max:(float )maxSheetVal
{
int longSize; /* numRows or numCols, whichever is the long side */
int index;
float thisVal;
float cellWidth; /* the width of a unit (N) to plot */
/* scale the plot */
PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) );
PStranslate(0.0, -minSheetVal);
longSize = [self numLocations];
cellWidth = r->size.width / (longSize);
/* now plot each square */
PSsetgray(NX_BLACK);
thisVal = [self valueOfLocation:0];
PSmoveto(cellWidth/2.0, thisVal);
for ( index = 0 ; index < longSize; index++){
thisVal = [self valueOfLocation:index];
PSlineto(cellWidth*index+ cellWidth/2.0, thisVal);
}
PSstroke();
return self;
}
/* we can position the bars anywhere inside the view by changing the
* insideRect parameter
*/
- calcRect:(NXRect *)r
ofBar:(int)n
insideRect:(NXRect *)boundingRect
usingMin:(float)minSheetVal
{
float thisVal;
float cellWidth; /* width of a unit (N) to plot */
thisVal = [self valueOfLocation:n];
if (COM_Flags.drawType == DRAW_V_BAR){
cellWidth = boundingRect->size.width / (3 * [self numLocations] + 1);
r->origin.x = n * 3 * cellWidth + cellWidth;
if (thisVal < 0.0){
r->origin.y = thisVal;
r->size.height = -thisVal;
}else{
r->origin.y = MAX(minSheetVal, 0.0);
r->size.height= thisVal-r->origin.y;
}
r->size.width = 2 * cellWidth;
} else /* assume h bar */ {
cellWidth = boundingRect->size.height / (3 * [self numLocations] + 1);
r->origin.y = n * 3 * cellWidth + cellWidth;
if (thisVal < 0.0){
r->origin.x = thisVal;
r->size.width = -thisVal;
}else{
r->origin.x = MAX(minSheetVal, 0.0);
r->size.width= thisVal - r->origin.x;
}
r->size.height = 2 * cellWidth;
}
return self;
}
/* this should probably be empty but because Kris asked for a bar/lines switch
* in the inspectorwe have the abillity to draw bars (vertical). Of course
* this makes the vertical bar drawing object almost codeless
*/
- renderBars:(NXRect *)r min:(float )minSheetVal max:(float )maxSheetVal
{
int longSize; /* numRows/numCols, whichever is long side */
int index;
NXRect barRect; /* bounding rectangle for bar in the chart */
NXColor HSBColor; /* in case randomBarColors */
longSize = [self numLocations];
if (COM_Flags.drawType == DRAW_V_BAR){
/* scale the plot */
PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) );
PStranslate(0.0, -minSheetVal);
} else /* assume h bar */ {
/* scale the plot */
PSscale(r->size.width / ( maxSheetVal - minSheetVal), 1.0 );
PStranslate( -minSheetVal, 0.0);
}
/* now plot each square */
for ( index = 0 ; index < longSize; index++){
if (index == highlightIndex)
NXSetColor(highlightColor);
else if (!COM_Flags.randomBarColors) {
PSsetgray ((1.0 / (longSize+2.0)) * (index +1));
} else {
HSBColor = NXConvertHSBToColor(
((float)index / (float)longSize),
1.0, 1.0);
NXSetColor(HSBColor);
}
[self calcRect:&barRect ofBar:index
insideRect:(NXRect *)r
usingMin:minSheetVal];
NXRectFill(&barRect);
}
return self;
}
/* spacing: Each plot is a width of 2N and each gap is a width of 1N
* total width is m*(2N+N) + N = 3mN+N = where m = number of bars
* Thus N = width / (3m+1)
*
* scaling: The scaling can make some stuff look pretty funny.
*/
- drawSelf:(NXRect *)r :(int)c
{
float minCellVal, maxCellVal; /* min & max of values to be plotted */
float meanCellVal; /* mean of the plotted values */
float minSheetVal,maxSheetVal;/* RangeOfNumbers will plot in graph */
NXRect rectOfPlot; /* bounds of rect that will hold plot */
NXSetColor(backgroundColor);
NXRectFill(&bounds);
PSsetgray(NX_BLACK);
switch (borderType) {
case NX_LINE:
NXFrameRect(&bounds);
break;
case NX_BEZEL:
NXDrawWhiteBezel(&bounds,&bounds);
break;
case NX_GROOVE:
NXDrawGroove(&bounds, &bounds);
break;
default: break;
}
rectOfPlot= bounds;
rectOfPlot.origin.x += hMargin;
rectOfPlot.origin.y += vMargin;
rectOfPlot.size.width -=2*hMargin;
rectOfPlot.size.height -=2*vMargin;
if ( NX_WIDTH(&bounds) < (2*hMargin) ||
NX_HEIGHT(&bounds) < (2*vMargin) )
return self;
[window disableFlushWindow];
PSgsave();
PSsetlinewidth(0.0);
PStranslate (hMargin, vMargin);
if (YES){
/* great we only have a single axis to work on */
[self calcMin:&minCellVal andMax:&maxCellVal andMean:&meanCellVal];
if (COM_Flags.autoScale){
/* give us a plot if they are all the same (but not 0.0) */
if (minCellVal == maxCellVal){
if (minCellVal > 0.0) minCellVal = 0.0;
else if (maxCellVal < 0.0) maxCellVal = 0.0;
}
/* figure out what min and the max should be on the sheet */
if (minCellVal == 0.0) minSheetVal= 0.0;
else minSheetVal= minCellVal - ((maxCellVal - minCellVal)*0.2);
/* we shouldn't have pushed this across the origin */
if (minSheetVal < 0.0 && minCellVal > 0.0) minSheetVal = 0.0;
if (maxCellVal == 0.0) maxSheetVal = 0.0;
else maxSheetVal= maxCellVal +((maxCellVal - minCellVal)* 0.2);
/* make sure we didn't go across the origin */
if (maxSheetVal > 0.0 && maxCellVal < 0.0) maxSheetVal = 0.0;
} else {
minSheetVal = minSheetSet;
maxSheetVal = maxSheetSet;
}
switch(COM_Flags.drawType){
case DRAW_H_BAR:
case DRAW_V_BAR:
[self renderBars:&rectOfPlot min:minSheetVal max:maxSheetVal];
break;
case DRAW_H_LINE:
break;
case DRAW_V_LINE:
[self renderVLines:&rectOfPlot min:minSheetVal max:maxSheetVal ];
break;
default:
break;
}
PSgrestore();
if (COM_Flags.drawFrame){
PSsetgray(NX_BLACK);
NXFrameRect(&rectOfPlot);
}
}
[window reenableFlushWindow];
return self;
}
@end